package com.yeziji.job.common.base;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.yeziji.constant.VariousStrPool;
import com.yeziji.job.business.jobInfo.base.JobInfo;
import com.yeziji.job.common.JobContext;
import com.yeziji.job.common.JobHolder;
import com.yeziji.job.common.log.YzjJobLogger;
import com.yeziji.job.constant.JobConstants;
import com.yeziji.job.constant.JobMisfirePolicyEnum;
import com.yeziji.job.utils.JobUtils;
import com.yeziji.utils.DataUtils;
import com.yeziji.utils.expansion.Opt2;
import lombok.extern.slf4j.Slf4j;
import org.quartz.InterruptableJob;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobKey;

import java.lang.reflect.Method;

/**
 * quartz job 执行基本类
 *
 * @author hwy
 * @since 2024/05/08 15:29
 **/
@Slf4j
public abstract class QuartzJobExecutorBase implements InterruptableJob {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 执行启用任务，如果 jobInfo 为空自动停止
        JobContextInfo jobContext = DataUtils.convertBean(context.getMergedJobDataMap().get(JobConstants.JOB_PARAMS), JobContextInfo.class);
        if (jobContext == null || jobContext.getJobInfo() == null) {
            JobKey jobKey = context.getJobDetail().getKey();
            JobUtils.paused(context.getScheduler(), jobKey);
            YzjJobLogger.warn("{} job info is null >>> auto paused", jobKey.getName());
            return;
        }

        // 继承线程信息
        if (JobContext.get() == null) {
            JobContext.update(jobContext.getFlag(), jobContext);
        }

        JobInfo jobInfo = jobContext.getJobInfo();
        // 从 bean 获取对应的方法后利用反射执行对应的 method; 如果获取不到 bean 那就去尝试构造类后再执行
        final String invokeTarget = jobInfo.getInvokeTarget();
        String invokeParams = jobInfo.getInvokeParams();
        Class<?> invokeClass = JobHolder.get(invokeTarget);
        Object bean = SpringUtil.getBean(invokeClass);

        try {
            YzjJobLogger.info(">>> [{}] start running: {} ...", jobContext.getFlag(), invokeParams);
            if (bean != null) {
                Method method = ReflectUtil.getMethodByName(invokeClass, invokeTarget);
                if (method == null) {
                    YzjJobLogger.warn(">>> [{}] not found invoke target exec method", invokeTarget);
                    return;
                }
                // check method params
                int parameterCount = method.getParameterCount();
                if (parameterCount > 0) {
                    // 这里赋值默认值
                    if (invokeParams == null) {
                        invokeParams = VariousStrPool.EMPTY;
                    }
                    ReflectUtil.invoke(bean, invokeTarget, invokeParams);
                } else {
                    ReflectUtil.invoke(bean, invokeTarget);
                }
            } else {
                Class<?> customClass = JobHolder.get(invokeTarget);
                if (customClass == null) {
                    YzjJobLogger.warn(">>> [{}] not found invoke target exec class", invokeTarget);
                    return;
                }
                Object target = customClass.getDeclaredConstructor().newInstance();
                ReflectUtil.invoke(target, invokeTarget, invokeParams);
            }
        } catch (Exception e) {
            YzjJobLogger.error("{} invoker error", invokeTarget, e);
            jobContext.appendException(e);
            // 异常继续执行
            JobExecutionException jobExecutionException = new JobExecutionException(e);
            jobExecutionException.setStackTrace(e.getStackTrace());
            JobMisfirePolicyEnum policyEnum = JobMisfirePolicyEnum.getByCode(jobInfo.getMisfirePolicy());
            if (policyEnum == JobMisfirePolicyEnum.IGNORE_MISFIRES) {
                jobContext.setRetryCount(Opt2.nullElse(jobContext.getRetryCount(), 0) + 1);
                Integer retryCount = jobContext.getRetryCount();
                if (retryCount != null && retryCount > 3) {
                    log.warn("[{}] 重试次数过多，不再重试", jobInfo.getName());
                    jobExecutionException.setRefireImmediately(false);
                    jobExecutionException.setUnscheduleAllTriggers(true);
                }
            }
            throw jobExecutionException;
        } finally {
            JobContext.update(jobContext.getFlag(), jobContext);
        }
    }
}